#!/bin/bash

# CI/CD 自动部署管理器 v1.0
# 支持 Git Push 触发自动构建和 Docker 部署
# 作者：基于 webhook_panel.sh 改进
# 版本：1.0

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m'

# 目录结构
BASE_DIR="/etc/deploy_tool"
TASKS_DIR="$BASE_DIR/tasks"
SCRIPTS_DIR="$BASE_DIR/scripts"
PID_DIR="$BASE_DIR/pids"
LOG_DIR="/var/log/deploy_tool"
SYSTEMD_DIR="/etc/systemd/system"
GLOBAL_CONF="$BASE_DIR/tool.conf"
BUILD_DIR="/opt/deploy_builds"
DOCKER_DIR="/opt/deploy_docker"

mkdir -p "$TASKS_DIR" "$SCRIPTS_DIR" "$PID_DIR" "$LOG_DIR" "$BUILD_DIR" "$DOCKER_DIR"

# 依赖检查和安装
check_dependencies() {
  local missing=()
  command -v python3 >/dev/null 2>&1 || missing+=("python3")
  command -v curl >/dev/null 2>&1 || missing+=("curl")
  command -v git >/dev/null 2>&1 || missing+=("git")
  command -v docker >/dev/null 2>&1 || missing+=("docker")
  
  if ! command -v ss >/dev/null 2>&1 && ! command -v netstat >/dev/null 2>&1 && ! command -v lsof >/dev/null 2>&1; then
    missing+=("ss或netstat或lsof")
  fi
  
  if [ ${#missing[@]} -gt 0 ]; then
    echo -e "${YELLOW}⚠️ 检测到缺少以下依赖: ${missing[*]}${NC}"
    
    # 自动安装基础依赖
    if command -v yum >/dev/null 2>&1; then
      echo -e "${YELLOW}正在通过 yum 安装基础依赖...${NC}"
      yum update -y >/dev/null 2>&1 || true
      yum install -y python3 curl git net-tools lsof >/dev/null 2>&1 || true
    elif command -v apt-get >/dev/null 2>&1; then
      echo -e "${YELLOW}正在通过 apt-get 安装基础依赖...${NC}"
      apt-get update >/dev/null 2>&1 && apt-get install -y python3 curl git net-tools lsof >/dev/null 2>&1 || true
    fi
    
    # Docker 安装检查
    if ! command -v docker >/dev/null 2>&1; then
      echo -e "${RED}❌ 未检测到 Docker，需要安装 Docker 才能使用部署功能${NC}"
      read -p "是否自动安装 Docker? (y/N): " install_docker
      if [[ "$install_docker" =~ ^[Yy]$ ]]; then
        install_docker_service
      else
        echo -e "${YELLOW}⚠️ 没有 Docker，部署功能将无法正常工作${NC}"
      fi
    else
      echo -e "${GREEN}✅ Docker 已安装${NC}"
      # 检查 Docker 服务状态
      if ! systemctl is-active docker >/dev/null 2>&1; then
        echo -e "${YELLOW}正在启动 Docker 服务...${NC}"
        systemctl start docker 2>/dev/null || true
        systemctl enable docker 2>/dev/null || true
      fi
    fi
  fi
  
  # 检查timeout命令
  if ! command -v timeout >/dev/null 2>&1; then
    echo -e "${YELLOW}⚠️ 未检测到timeout命令，端口检测可能较慢${NC}"
  fi
}

# Docker 安装函数
install_docker_service() {
  echo -e "${CYAN}正在安装 Docker...${NC}"
  
  if command -v yum >/dev/null 2>&1; then
    # CentOS/RHEL
    echo -e "${YELLOW}检测到 CentOS/RHEL 系统，使用 yum 安装 Docker...${NC}"
    yum install -y yum-utils
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  elif command -v apt-get >/dev/null 2>&1; then
    # Ubuntu/Debian
    echo -e "${YELLOW}检测到 Ubuntu/Debian 系统，使用 apt 安装 Docker...${NC}"
    apt-get update
    apt-get install -y ca-certificates curl gnupg lsb-release
    mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
    apt-get update
    apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  else
    echo -e "${RED}❌ 不支持的系统类型，请手动安装 Docker${NC}"
    return 1
  fi
  
  # 启动并启用 Docker 服务
  systemctl start docker
  systemctl enable docker
  
  # 验证安装
  if command -v docker >/dev/null 2>&1 && docker --version >/dev/null 2>&1; then
    echo -e "${GREEN}✅ Docker 安装成功${NC}"
    docker --version
  else
    echo -e "${RED}❌ Docker 安装失败${NC}"
    return 1
  fi
}

# 工具函数
ensure_global_port() {
  local start_port=9000
  # 若已存在全局端口配置，直接复用并提示
  if [ -f "$GLOBAL_CONF" ]; then
    source "$GLOBAL_CONF"
    if [[ "$GLOBAL_PORT" =~ ^[0-9]+$ ]]; then
      echo -e "${GREEN}✅ 使用部署服务全局端口 ${GLOBAL_PORT}${NC}" 1>&2
      echo "$GLOBAL_PORT"
      return
    fi
  fi

  echo -e "${CYAN}正在自动分配部署服务全局端口...${NC}" 1>&2
  local chosen=""
  local candidates=()
  for p in $(seq $start_port $((start_port+20))); do
    if ! is_port_in_use "$p"; then
      candidates+=("$p")
      [ ${#candidates[@]} -ge 3 ] && break
    fi
  done
  if [ ${#candidates[@]} -eq 0 ]; then
    # 若未找到，直接使用起始端口
    chosen=$start_port
  else
    echo -e "${GREEN}✅ 发现可用端口: ${candidates[*]}${NC}" 1>&2
    chosen=${candidates[0]}
  fi

  echo -e "${GREEN}✅ 端口 ${chosen} 可用，已全局配置${NC}" 1>&2
  echo "GLOBAL_PORT=\"$chosen\"" > "$GLOBAL_CONF"
  echo "$chosen"
}

get_server_ip() {
  local ip
  ip=$(curl -s ipinfo.io/ip 2>/dev/null)
  [ -z "$ip" ] && ip=$(hostname -I 2>/dev/null | awk '{print $1}')
  [ -z "$ip" ] && ip="127.0.0.1"
  echo "$ip"
}

is_port_in_use() {
  local port="$1"
  local result=1
  
  # 使用timeout防止长时间等待
  if command -v timeout >/dev/null 2>&1; then
    if command -v ss >/dev/null 2>&1; then
      timeout 2s ss -tln 2>/dev/null | awk '{print $4}' | grep -E ":$port$" -q && result=0
    elif command -v netstat >/dev/null 2>&1; then
      timeout 2s netstat -tln 2>/dev/null | awk '{print $4}' | grep -E ":$port$" -q && result=0
    elif command -v lsof >/dev/null 2>&1; then
      timeout 2s lsof -iTCP -sTCP:LISTEN -P 2>/dev/null | awk '{print $9}' | grep -E ":$port$" -q && result=0
    fi
  else
    # 没有timeout命令时的备用方案
    if command -v ss >/dev/null 2>&1; then
      ss -tln 2>/dev/null | awk '{print $4}' | grep -E ":$port$" -q && result=0
    elif command -v netstat >/dev/null 2>&1; then
      netstat -tln 2>/dev/null | awk '{print $4}' | grep -E ":$port$" -q && result=0
    elif command -v lsof >/dev/null 2>&1; then
      lsof -iTCP -sTCP:LISTEN -P 2>/dev/null | awk '{print $9}' | grep -E ":$port$" -q && result=0
    fi
  fi
  
  return $result
}

generate_token() {
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -hex 12
  else
    echo "$RANDOM$(date +%s%N)$RANDOM" | md5sum | awk '{print $1}'
  fi
}

# 通知函数
send_notification() {
  local webhook_url="$1"
  local title="$2"
  local message="$3"
  local status="$4" # success, warning, error
  
  if [ -z "$webhook_url" ]; then
    return 0
  fi
  
  local color_map
  case "$status" in
    "success") color_map="green" ;;
    "warning") color_map="yellow" ;;
    "error") color_map="red" ;;
    *) color_map="blue" ;;
  esac
  
  # 构建通知消息 (支持飞书、企业微信、钉钉格式)
  local json_message
  if [[ "$webhook_url" == *"open.feishu.cn"* ]]; then
    # 飞书格式
    json_message=$(cat << EOF
{
  "msg_type": "post",
  "content": {
    "post": {
      "zh_cn": {
        "title": "$title",
        "content": [
          [{
            "tag": "text",
            "text": "$message"
          }],
          [{
            "tag": "text",
            "text": "时间: $(date '+%Y-%m-%d %H:%M:%S')"
          }]
        ]
      }
    }
  }
}
EOF
    )
  elif [[ "$webhook_url" == *"qyapi.weixin.qq.com"* ]]; then
    # 企业微信格式
    json_message=$(cat << EOF
{
  "msgtype": "markdown",
  "markdown": {
    "content": "## $title\n\n$message\n\n**时间**: $(date '+%Y-%m-%d %H:%M:%S')"
  }
}
EOF
    )
  else
    # 钉钉格式
    json_message=$(cat << EOF
{
  "msgtype": "markdown",
  "markdown": {
    "title": "$title",
    "text": "## $title\n\n$message\n\n**时间**: $(date '+%Y-%m-%d %H:%M:%S')"
  }
}
EOF
    )
  fi
  
  # 发送通知
  curl -s -X POST \
    -H "Content-Type: application/json" \
    -d "$json_message" \
    "$webhook_url" >/dev/null 2>&1 || true
}

# 读取多行部署配置
read_multiline_deploy_config() {
  local end_marker="$1"
  local content=""
  
  # 显示输入说明
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 1>&2
  echo -e "${WHITE}📋 输入部署配置信息${NC}" 1>&2
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 1>&2
  echo -e "${YELLOW}📝 配置说明：${NC}" 1>&2
  echo -e "  ${WHITE}请按照以下格式输入部署配置（每行一个配置项）：${NC}" 1>&2
  echo -e "  ${CYAN}REPO_URL=https://github.com/user/project.git${NC}" 1>&2
  echo -e "  ${CYAN}BRANCH=main${NC}" 1>&2
  echo -e "  ${CYAN}DOCKERFILE_PATH=Dockerfile${NC}" 1>&2
  echo -e "  ${CYAN}IMAGE_NAME=myapp${NC}" 1>&2
  echo -e "  ${CYAN}CONTAINER_NAME=myapp-container${NC}" 1>&2
  echo -e "  ${CYAN}CONTAINER_PORT=8080${NC}" 1>&2
  echo -e "  ${CYAN}HOST_PORT=80${NC}" 1>&2
  echo -e "  ${CYAN}BUILD_COMMAND=npm install && npm run build${NC}" 1>&2
  echo -e "  ${CYAN}NOTIFICATION_WEBHOOK=https://open.feishu.cn/xxx${NC}" 1>&2
  echo -e "  ${WHITE}输入完成后，单独一行输入 ${CYAN}${end_marker}${NC} 结束${NC}" 1>&2
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" 1>&2
  echo -e "${WHITE}开始输入配置（输入 ${end_marker} 结束）：${NC}" 1>&2
  
  # 开始读取用户输入
  while IFS= read -r line; do
    [ "$line" = "$end_marker" ] && break
    content+="$line\n"
  done
  

# 生成部署用 Python 服务脚本
create_deploy_python_server() {
  local port="$1"
  local py_file="$SCRIPTS_DIR/deploy_server.py"
  cat > "$py_file" << PYEOF
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import threading, subprocess, time, os, shutil, json, sys
import urllib.parse
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from urllib.parse import urlparse, parse_qs

# 设置环境变量
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['LANG'] = 'zh_CN.UTF-8'
os.environ['LC_ALL'] = 'zh_CN.UTF-8'

PORT = ${port}
BASE_DIR = r"${BASE_DIR}"
TASKS_DIR = os.path.join(BASE_DIR, 'tasks')
LOG_DIR = r"${LOG_DIR}"
BUILD_DIR = r"${BUILD_DIR}"
DOCKER_DIR = r"${DOCKER_DIR}"
os.makedirs(LOG_DIR, exist_ok=True)
os.makedirs(BUILD_DIR, exist_ok=True)
os.makedirs(DOCKER_DIR, exist_ok=True)

def _safe_get(d: dict, path: list, default=""):
    cur = d
    for key in path:
        if not isinstance(cur, dict):
            return default
        cur = cur.get(key, {})
    return cur if cur != {} else default

def _strip_branch(ref: str) -> str:
    return (ref or "").replace('refs/heads/', '')

def _get_files_changed(commit_data: dict) -> str:
    """从提交数据中提取变更的文件列表"""
    if not isinstance(commit_data, dict):
        return ""
    
    files = []
    
    # 获取新增文件
    added = commit_data.get('added', [])
    if isinstance(added, list) and added:
        files.extend([f"+ {f}" for f in added])
    
    # 获取修改文件
    modified = commit_data.get('modified', [])
    if isinstance(modified, list) and modified:
        files.extend([f"* {f}" for f in modified])
    
    # 获取删除文件
    removed = commit_data.get('removed', [])
    if isinstance(removed, list) and removed:
        files.extend([f"- {f}" for f in removed])
    
    return "\n".join(files) if files else ""

def parse_git_payload(data: dict) -> dict:
    """将 GitHub/GitLab/Gitee/Coding 的 Webhook 负载统一解析为通用字段"""
    if not isinstance(data, dict):
        return {}

    platform = ""
    repo_name = repo_url = branch = commit_id = commit_message = author = committer = ""
    files_changed = ""

    # 首先通过 repository 字段识别平台
    if 'repository' in data:
        repo = data.get('repository') or {}
        repo_name = repo.get('name', '')
        repo_url = repo.get('html_url') or repo.get('url') or repo.get('web_url') or ""
        
        # 通过URL识别平台
        url_l = (repo_url or '').lower()
        if 'github.com' in url_l:
            platform = 'github'
        elif 'gitee.com' in url_l:
            platform = 'gitee'
        elif 'coding.net' in url_l or 'dev.tencent.com' in url_l:
            platform = 'coding'
        elif 'gitlab' in url_l:
            platform = 'gitlab'
        
        # 通过数据结构进一步确认
        if not platform:
            if 'head_commit' in data:
                platform = 'github'  # GitHub特有字段
            elif 'project' in data:
                platform = 'gitlab'  # GitLab特有字段
            elif 'commits' in data and 'repository' in data:
                platform = 'github'

        branch = _strip_branch(data.get('ref', ''))
        commit_id = data.get('after', '') or data.get('head_commit', {}).get('id', '')

        # 获取提交信息
        head_commit = data.get('head_commit')
        if isinstance(head_commit, dict) and head_commit:
            commit_message = head_commit.get('message', '')
            author = _safe_get(head_commit, ['author', 'name']) or _safe_get(head_commit, ['author', 'username'])
            committer = _safe_get(head_commit, ['committer', 'name']) or _safe_get(head_commit, ['committer', 'username'])
            files_changed = _get_files_changed(head_commit)
        else:
            commits = data.get('commits', [])
            if commits and isinstance(commits, list) and len(commits) > 0:
                first_commit = commits[0]
                commit_message = first_commit.get('message', '')
                author = _safe_get(first_commit, ['author', 'name']) or _safe_get(first_commit, ['author', 'username'])
                committer = _safe_get(first_commit, ['committer', 'name']) or _safe_get(first_commit, ['committer', 'username'])
                files_changed = _get_files_changed(first_commit)

        return {
            'platform': platform or 'unknown',
            'repo_name': repo_name,
            'repo_url': repo_url,
            'branch': branch,
            'commit_id': commit_id,
            'commit_message': commit_message,
            'author': author,
            'committer': committer,
            'files_changed': files_changed,
        }

    # GitLab特有结构：使用 project 字段
    elif 'project' in data:
        platform = 'gitlab'
        repo_name = _safe_get(data, ['project', 'name']) or ""
        repo_url = _safe_get(data, ['project', 'web_url']) or _safe_get(data, ['project', 'url']) or ""
        branch = _strip_branch(data.get('ref', ''))
        commit_id = data.get('after', '')
        
        commits = data.get('commits', [])
        if commits and isinstance(commits, list) and len(commits) > 0:
            first_commit = commits[0]
            commit_message = first_commit.get('message', '')
            author = _safe_get(first_commit, ['author', 'name']) or _safe_get(first_commit, ['author', 'username'])
            committer = _safe_get(first_commit, ['committer', 'name']) or _safe_get(first_commit, ['committer', 'username'])
            files_changed = _get_files_changed(first_commit)

        return {
            'platform': platform,
            'repo_name': repo_name,
            'repo_url': repo_url,
            'branch': branch,
            'commit_id': commit_id,
            'commit_message': commit_message,
            'author': author,
            'committer': committer,
            'files_changed': files_changed,
        }

    return {}

def send_notification(webhook_url, title, message, status="info"):
    """发送通知到飞书/企业微信/钉钉"""
    if not webhook_url:
        return
    
    try:
        import requests
    except ImportError:
        # 使用curl发送
        subprocess.run(['curl', '-s', '-X', 'POST', '-H', 'Content-Type: application/json', 
                       '-d', json.dumps({"text": f"{title}: {message}"}), webhook_url], 
                      capture_output=True)
        return
    
    # 构建不同平台的消息格式
    if 'open.feishu.cn' in webhook_url:
        # 飞书格式
        payload = {
            "msg_type": "post",
            "content": {
                "post": {
                    "zh_cn": {
                        "title": title,
                        "content": [[
                            {"tag": "text", "text": message},
                            {"tag": "text", "text": f"\n时间: {time.strftime('%Y-%m-%d %H:%M:%S')}"}
                        ]]
                    }
                }
            }
        }
    elif 'qyapi.weixin.qq.com' in webhook_url:
        # 企业微信格式
        payload = {
            "msgtype": "markdown",
            "markdown": {
                "content": f"## {title}\n\n{message}\n\n**时间**: {time.strftime('%Y-%m-%d %H:%M:%S')}"
            }
        }
    else:
        # 钉钉格式
        payload = {
            "msgtype": "markdown",
            "markdown": {
                "title": title,
                "text": f"## {title}\n\n{message}\n\n**时间**: {time.strftime('%Y-%m-%d %H:%M:%S')}"
            }
        }
    
    try:
        requests.post(webhook_url, json=payload, timeout=10)
    except:
        pass

def execute_build_deploy(config, git_data, log_file):
    """执行构建和部署流程"""
    start_time = time.time()
    
    def log_and_notify(message, level="info"):
        timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
        log_file.write(f"[{timestamp}] {message}\n")
        log_file.flush()
        
        if 'NOTIFICATION_WEBHOOK' in config and config['NOTIFICATION_WEBHOOK']:
            status_map = {"info": "info", "error": "error", "success": "success"}
            send_notification(config['NOTIFICATION_WEBHOOK'], "🚀 CI/CD 部署通知", message, status_map.get(level, "info"))
    
    try:
        # 1. 发送开始通知
        repo_info = f"仓库: {git_data.get('repo_name', 'unknown')}\n分支: {git_data.get('branch', 'unknown')}\n提交: {git_data.get('commit_id', '')[:8]}\n作者: {git_data.get('author', 'unknown')}"
        log_and_notify(f"🔄 开始构建部署\n{repo_info}")
        
        # 2. 检查必需配置
        required_configs = ['REPO_URL', 'IMAGE_NAME', 'CONTAINER_NAME']
        for key in required_configs:
            if key not in config or not config[key]:
                raise Exception(f"缺少必需的配置项: {key}")
        
        # 3. 克隆/更新代码
        build_path = os.path.join(BUILD_DIR, config['IMAGE_NAME'])
        if os.path.exists(build_path):
            log_and_notify("🔄 更新代码库...")
            subprocess.run(['git', 'pull'], cwd=build_path, check=True, capture_output=True, text=True)
        else:
            log_and_notify("📥 克隆代码库...")
            subprocess.run(['git', 'clone', config['REPO_URL'], build_path], check=True, capture_output=True, text=True)
        
        # 4. 切换分支
        if 'BRANCH' in config and config['BRANCH']:
            log_and_notify(f"🔀 切换到分支: {config['BRANCH']}")
            subprocess.run(['git', 'checkout', config['BRANCH']], cwd=build_path, check=True, capture_output=True, text=True)
        
        # 5. 执行构建命令（如果有）
        if 'BUILD_COMMAND' in config and config['BUILD_COMMAND']:
            log_and_notify(f"🔨 执行构建命令: {config['BUILD_COMMAND']}")
            subprocess.run(config['BUILD_COMMAND'], shell=True, cwd=build_path, check=True, capture_output=True, text=True)
        
        # 6. 构建 Docker 镜像
        dockerfile_path = config.get('DOCKERFILE_PATH', 'Dockerfile')
        if not os.path.exists(os.path.join(build_path, dockerfile_path)):
            raise Exception(f"未找到 Dockerfile: {dockerfile_path}")
        
        # 使用 Git 提交 ID 作为镜像标签
        commit_tag = git_data.get('commit_id', '')[:8] or 'latest'
        image_tag = f"{config['IMAGE_NAME']}:{commit_tag}"
        latest_tag = f"{config['IMAGE_NAME']}:latest"
        
        log_and_notify(f"🐳 构建 Docker 镜像: {image_tag}")
        subprocess.run(['docker', 'build', '-f', dockerfile_path, '-t', image_tag, '-t', latest_tag, '.'], 
                      cwd=build_path, check=True, capture_output=True, text=True)
        
        # 7. 检查是否有相同版本的容器在运行
        container_name = config['CONTAINER_NAME']
        
        # 检查容器是否存在
        result = subprocess.run(['docker', 'ps', '-a', '--filter', f'name={container_name}', '--format', '{{.ID}}'], 
                               capture_output=True, text=True)
        existing_containers = result.stdout.strip().split('\n') if result.stdout.strip() else []
        
        if existing_containers and existing_containers[0]:
            log_and_notify(f"🛑 停止并删除现有容器: {container_name}")
            for container_id in existing_containers:
                subprocess.run(['docker', 'stop', container_id], capture_output=True)
                subprocess.run(['docker', 'rm', container_id], capture_output=True)
        
        # 8. 启动新容器
        docker_run_cmd = ['docker', 'run', '-d', '--name', container_name]
        
        if 'CONTAINER_PORT' in config and 'HOST_PORT' in config:
            docker_run_cmd.extend(['-p', f"{config['HOST_PORT']}:{config['CONTAINER_PORT']}"])
        
        # 添加环境变量（如果有）
        if 'ENV_VARS' in config and config['ENV_VARS']:
            for env_var in config['ENV_VARS'].split(','):
                if '=' in env_var:
                    docker_run_cmd.extend(['-e', env_var.strip()])
        
        docker_run_cmd.append(latest_tag)
        
        log_and_notify(f"🚀 启动新容器: {container_name}")
        result = subprocess.run(docker_run_cmd, capture_output=True, text=True, check=True)
        container_id = result.stdout.strip()[:12]
        
        # 9. 验证容器状态
        time.sleep(2)  # 等待容器启动
        result = subprocess.run(['docker', 'ps', '--filter', f'name={container_name}', '--format', '{{.Status}}'], 
                               capture_output=True, text=True)
        if 'Up' not in result.stdout:
            raise Exception("容器启动失败")
        
        # 10. 清理旧镜像（保留最新的5个版本）
        log_and_notify("🧹 清理旧镜像...")
        result = subprocess.run(['docker', 'images', config['IMAGE_NAME'], '--format', '{{.ID}}'], 
                               capture_output=True, text=True)
        image_ids = result.stdout.strip().split('\n')
        if len(image_ids) > 5:
            for old_image in image_ids[5:]:
                subprocess.run(['docker', 'rmi', old_image], capture_output=True)
        
        # 11. 发送成功通知
        elapsed_time = int(time.time() - start_time)
        success_msg = f"✅ 部署成功完成\n容器ID: {container_id}\n镜像标签: {commit_tag}\n耗时: {elapsed_time}秒"
        log_and_notify(success_msg, "success")
        
    except Exception as e:
        error_msg = f"❌ 部署失败: {str(e)}"
        log_and_notify(error_msg, "error")
        raise

def run_deploy_task(config_path, token, git_data=None):
    """运行部署任务"""
    log_path = os.path.join(LOG_DIR, f"deploy_{token}.log")
    start_ts = time.strftime('%Y-%m-%d %H:%M:%S')
    
    with open(log_path, 'a', buffering=1, encoding='utf-8') as f:
        f.write(f"[{start_ts}] WebHook触发部署任务，token={token}\n")
        if git_data:
            f.write(f"Git数据: {git_data}\n")
        
        try:
            # 读取部署配置
            config = {}
            with open(config_path, 'r', encoding='utf-8') as cf:
                for line in cf:
                    line = line.strip()
                    if line and '=' in line and not line.startswith('#'):
                        key, value = line.split('=', 1)
                        config[key.strip()] = value.strip().strip('"\'')
            
            # 执行部署
            execute_build_deploy(config, git_data or {}, f)
            
        except Exception as e:
            f.write(f"[ERROR] 部署异常: {e}\n")
            # 发送错误通知
            if 'NOTIFICATION_WEBHOOK' in config and config['NOTIFICATION_WEBHOOK']:
                send_notification(config['NOTIFICATION_WEBHOOK'], "❌ 部署失败", str(e), "error")
        
        end_ts = time.strftime('%Y-%m-%d %H:%M:%S')
        f.write(f"[{end_ts}] 部署任务结束\n")

class Handler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        return
    
    def _ok(self, msg):
        data = msg.encode('utf-8')
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain; charset=utf-8')
        self.send_header('Content-Length', str(len(data)))
        self.send_header('Connection', 'close')
        self.end_headers()
        try:
            self.wfile.write(data)
        except Exception:
            pass
    
    def _not_found(self):
        data = b'Not Found'
        self.send_response(404)
        self.send_header('Content-Type', 'text/plain; charset=utf-8')
        self.send_header('Content-Length', str(len(data)))
        self.send_header('Connection', 'close')
        self.end_headers()
        try:
            self.wfile.write(data)
        except Exception:
            pass
    
    def do_GET(self):
        self._handle({})
    
    def do_POST(self):
        # 读取请求体，解析Git平台数据
        git_data = {}
        try:
            length = int(self.headers.get('Content-Length', '0'))
            if length > 0:
                body = self.rfile.read(length).decode('utf-8')
                try:
                    data = json.loads(body)
                    parsed = parse_git_payload(data)
                    if parsed:
                        git_data.update(parsed)
                except json.JSONDecodeError:
                    pass
        except Exception:
            pass
        self._handle(git_data)
    
    def _handle(self, git_data=None):
        parsed = urlparse(self.path)
        path = parsed.path
        
        # /deploy/<token>
        parts = [p for p in path.split('/') if p]
        if len(parts) == 2 and parts[0] == 'deploy':
            token = parts[1]
            try:
                for name in os.listdir(TASKS_DIR):
                    if not (name.startswith('deploy_') and name.endswith('.conf')):
                        continue
                    id_part = name[7:-5]  # deploy_ 前缀，.conf 后缀
                    if not id_part.isdigit():
                        continue
                    conf_path = os.path.join(TASKS_DIR, name)
                    config_path = os.path.join(TASKS_DIR, name.replace('.conf', '_config.txt'))
                    
                    task_token = None
                    with open(conf_path, 'r') as cf:
                        for line in cf:
                            line = line.strip()
                            if line.startswith('TOKEN='):
                                task_token = line.split('=',1)[1].strip().strip('"')
                                break
                    
                    if task_token == token and os.path.isfile(config_path):
                        threading.Thread(target=run_deploy_task, args=(config_path, token, git_data), daemon=True).start()
                        response = f'OK: Deploy task started for {git_data.get("repo_name", "unknown")}\n'
                        self._ok(response)
                        return
            except Exception:
                pass
            self._not_found()
        elif path == "/healthz":
            self._ok('Deploy Service OK')
        else:
            self._not_found()

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
    daemon_threads = True

if __name__ == '__main__':
    httpd = ThreadingHTTPServer(('0.0.0.0', PORT), Handler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
PYEOF
  chmod +x "$py_file"
}

# 创建 systemd 服务
create_deploy_systemd_service() {
  local py_file="$SCRIPTS_DIR/deploy_server.py"
  local svc="deploy_tool.service"
  cat > "$SYSTEMD_DIR/$svc" << SVC
[Unit]
Description=CI/CD Deploy Tool Server
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
ExecStart=/usr/bin/python3 ${py_file}
Restart=always
RestartSec=3
User=root

[Install]
WantedBy=multi-user.target
SVC
  systemctl daemon-reload 2>/dev/null || true
}

# 启动部署服务
start_deploy_service() {
  local port="$1"
  local py_file="$SCRIPTS_DIR/deploy_server.py"
  
  if command -v systemctl >/dev/null 2>&1; then
    echo -e "${YELLOW}  使用systemd启动部署服务...${NC}"
    systemctl enable --now "deploy_tool.service" >/dev/null 2>&1 || systemctl start "deploy_tool.service" >/dev/null 2>&1
  else
    echo -e "${YELLOW}  使用后台进程启动部署服务...${NC}"
    nohup python3 "$py_file" > "$LOG_DIR/deploy_server.log" 2>&1 &
    echo $! > "$PID_DIR/deploy_server.pid"
  fi
  
  echo -e "${YELLOW}  等待服务启动...${NC}"
  sleep 2
  
  echo -e "${YELLOW}  检查服务状态...${NC}"
  if is_port_in_use "$port"; then
    echo -e "${GREEN}✅ 部署服务已启动${NC}"
  else
    echo -e "${RED}❌ 部署服务启动失败，请检查日志: $LOG_DIR/deploy_server.log${NC}"
  fi
}

# 停止部署服务
stop_deploy_service() {
  if command -v systemctl >/dev/null 2>&1; then
    systemctl stop deploy_tool.service 2>/dev/null || true
  else
    if [ -f "$PID_DIR/deploy_server.pid" ]; then
      kill $(cat "$PID_DIR/deploy_server.pid") 2>/dev/null || true
      rm -f "$PID_DIR/deploy_server.pid"
    fi
  fi
}

# 添加部署任务
add_deploy_task() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}➕ 新增 CI/CD 部署任务${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  
  while true; do
    read -p "任务名称: " task_name
    echo

    [ -z "$task_name" ] && echo -e "${RED}❌ 任务名称不能为空${NC}" && continue

    # 检查任务名称是否已存在
    local name_exists=false
    for conf in "$TASKS_DIR"/*.conf; do
      [ -f "$conf" ] || continue
      if grep -q "TASK_NAME=\"$task_name\"" "$conf" 2>/dev/null; then
        echo -e "${RED}❌ 任务名称 '$task_name' 已存在，请重新输入${NC}"
        name_exists=true
        break
      fi
    done
    
    [ "$name_exists" = false ] && break
  done

  local task_id=$(date +%s)
  local token=$(generate_token)
  local config_path="$TASKS_DIR/deploy_${task_id}_config.txt"
  local conf_path="$TASKS_DIR/deploy_${task_id}.conf"
  local log_path="$LOG_DIR/deploy_${token}.log"
  
  # 读取部署配置
  local deploy_config
  deploy_config=$(read_multiline_deploy_config "END")
  if [ -z "$deploy_config" ]; then
    echo -e "${YELLOW}⚠️ 未输入部署配置，已取消${NC}"; return
  fi

  # 保存部署配置
  printf "%s\n" "$deploy_config" > "$config_path"
  
  # 验证必需配置项
  if ! grep -q "REPO_URL=" "$config_path" || ! grep -q "IMAGE_NAME=" "$config_path" || ! grep -q "CONTAINER_NAME=" "$config_path"; then
    echo -e "${RED}❌ 缺少必需的配置项 (REPO_URL, IMAGE_NAME, CONTAINER_NAME)${NC}"
    rm -f "$config_path"
    return 1
  fi

  echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🚀 部署服务配置${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  
  echo -e "${YELLOW}正在分配全局端口...${NC}"
  local global_port=$(ensure_global_port)
  echo
  
  echo -e "${YELLOW}正在生成部署服务脚本...${NC}"
  create_deploy_python_server "$global_port"
  
  if command -v systemctl >/dev/null 2>&1; then
    echo -e "${YELLOW}正在创建systemd服务...${NC}"
    create_deploy_systemd_service
  else
    echo -e "${YELLOW}未检测到systemd，将使用后台进程模式...${NC}"
  fi
  
  echo -e "${YELLOW}正在启动部署服务...${NC}"
  start_deploy_service "$global_port"

  # 保存任务配置
  cat > "$conf_path" << EOF
TASK_ID="$task_id"
TASK_NAME="$task_name"
PORT="$global_port"
TOKEN="$token"
CONFIG_PATH="$config_path"
LOG_PATH="$log_path"
PY_FILE="$SCRIPTS_DIR/deploy_server.py"
EOF

  local ip=$(get_server_ip)
  echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🎉 部署任务创建成功${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${GREEN}✅ 任务创建成功${NC}"
  echo -e "${CYAN}WebHook 地址: ${WHITE}http://$ip:${global_port}/deploy/${token}${NC}"
  echo -e "${CYAN}日志文件: ${WHITE}$log_path${NC}"
  echo -e "${CYAN}测试部署: ${WHITE}curl -s -X POST 'http://$ip:${global_port}/deploy/${token}'${NC}"
  echo -e "${CYAN}配置Git WebHook: ${WHITE}复制上述WebHook地址到Git仓库设置中${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${YELLOW}📋 部署配置预览:${NC}"
  echo -e "${WHITE}$(cat "$config_path" | sed 's/^/    /')${NC}"
}

# 列出部署任务
list_deploy_tasks() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}📋 CI/CD 部署任务列表${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  local any=0
  for conf in "$TASKS_DIR"/deploy_*.conf; do
    [ -f "$conf" ] || continue
    # 仅展示按任务ID命名的配置
    base=$(basename "$conf")
    if ! [[ "$base" =~ ^deploy_[0-9]+\.conf$ ]]; then
      continue
    fi
    any=1
    source "$conf"
    local status="未运行"
    local gp=""
    if [ -f "$GLOBAL_CONF" ]; then source "$GLOBAL_CONF"; gp="$GLOBAL_PORT"; fi
    is_port_in_use "${gp:-$PORT}" && status="运行中"
    echo -e "${WHITE}任务: ${TASK_NAME}${NC}"
    echo -e "  ID: ${CYAN}${TASK_ID}${NC}"
    echo -e "  端口: ${CYAN}${gp:-$PORT}${NC}    状态: ${CYAN}${status}${NC}"
    echo -e "  WebHook地址: ${CYAN}http://$(get_server_ip):${gp:-$PORT}/deploy/${TOKEN}${NC}"
    echo -e "  配置文件: ${CYAN}${CONFIG_PATH}${NC}"
    echo -e "  日志文件: ${CYAN}${LOG_PATH}${NC}"
    
    # 显示部署配置摘要
    if [ -f "$CONFIG_PATH" ]; then
      local repo_url=$(grep "REPO_URL=" "$CONFIG_PATH" | cut -d'=' -f2- | tr -d '"\''')
      local image_name=$(grep "IMAGE_NAME=" "$CONFIG_PATH" | cut -d'=' -f2- | tr -d '"\''')
      local container_name=$(grep "CONTAINER_NAME=" "$CONFIG_PATH" | cut -d'=' -f2- | tr -d '"\''')
      echo -e "  仓库: ${CYAN}${repo_url}${NC}"
      echo -e "  镜像: ${CYAN}${image_name}${NC}"
      echo -e "  容器: ${CYAN}${container_name}${NC}"
    fi
    echo
  done
  [ $any -eq 0 ] && echo -e "${YELLOW}暂无部署任务${NC}"
}

# 选择任务交互函数
select_deploy_task_interactive() {
  local confs=()
  for cf in "$TASKS_DIR"/deploy_*.conf; do
    [ -f "$cf" ] || continue
    base=$(basename "$cf")
    [[ "$base" =~ ^deploy_[0-9]+\.conf$ ]] && confs+=("$cf")
  done
  if [ ${#confs[@]} -eq 0 ]; then
    echo -e "${YELLOW}暂无部署任务${NC}"; return 1
  fi
  echo -e "${WHITE}可用任务：${NC}"
  local i=1
  for cf in "${confs[@]}"; do
    source "$cf"
    echo -e "${CYAN}${i}.${NC} ${WHITE}${TASK_NAME}${NC} (${CYAN}ID:${TASK_ID}${NC})"
    i=$((i+1))
  done
  read -p "请选择任务编号: " idx
  if ! [[ "$idx" =~ ^[0-9]+$ ]] || [ "$idx" -lt 1 ] || [ "$idx" -gt ${#confs[@]} ]; then
    echo -e "${RED}❌ 无效选择${NC}"; return 1
  fi
  SELECTED_CONF="${confs[$((idx-1))]}"
  source "$SELECTED_CONF"
  return 0
}

# 测试部署任务
test_deploy_task() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🧪 测试 CI/CD 部署任务${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  select_deploy_task_interactive || return
  local gp="${PORT}"
  if [ -f "$GLOBAL_CONF" ]; then source "$GLOBAL_CONF"; gp="$GLOBAL_PORT"; fi
  local url="http://$(get_server_ip):${gp}/deploy/${TOKEN}"
  echo -e "${CYAN}触发部署: ${WHITE}${url}${NC}"
  
  # 发送测试请求
  local response
  response=$(curl -s -X POST "$url" 2>&1)
  if [[ "$response" == *"OK: Deploy task started"* ]]; then
    echo -e "${GREEN}✅ 部署任务已触发${NC}"
  else
    echo -e "${RED}❌ 触发失败: $response${NC}"
  fi
  
  sleep 2
  
  # 显示实时日志
  if [ -f "$LOG_PATH" ]; then
    echo -e "${YELLOW}实时部署日志：${NC}"
    tail -n 20 "$LOG_PATH" | sed -e 's/^/  /'
  else
    echo -e "${YELLOW}暂无日志${NC}"
  fi
}

# 查看部署日志
view_deploy_logs() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}📜 查看 CI/CD 部署日志${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  select_deploy_task_interactive || return
  echo -e "${WHITE}1.${NC} 最近50行"
  echo -e "${WHITE}2.${NC} 实时跟随 (Ctrl+C 退出)"
  read -p "请选择 [1-2] (回车默认2): " c
  [ -z "$c" ] && c="2"
  case "$c" in
    1)
      if [ -f "$LOG_PATH" ]; then tail -n 50 "$LOG_PATH"; else echo -e "${YELLOW}暂无日志${NC}"; fi
      ;;
    2)
      if [ -f "$LOG_PATH" ]; then 
        echo -e "${CYAN}开始实时监控部署日志，等待新内容... (Ctrl+C 退出)${NC}"
        tail -n 50 -f "$LOG_PATH"
      else 
        echo -e "${YELLOW}日志文件不存在，等待创建... (Ctrl+C 退出)${NC}"
        echo -e "${CYAN}正在监控: ${LOG_PATH}${NC}"
        touch "$LOG_PATH" 2>/dev/null
        tail -f "$LOG_PATH"
      fi
      ;;
    *) echo -e "${RED}❌ 无效选择${NC}";;
  esac
}

# 查看部署配置
view_deploy_config() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🧾 查看部署配置${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  select_deploy_task_interactive || return
  echo -e "${WHITE}任务: ${TASK_NAME}${NC}"
  echo -e "${WHITE}配置文件: ${CYAN}${CONFIG_PATH}${NC}"
  if [ -f "$CONFIG_PATH" ]; then
    echo -e "${WHITE}配置内容：${NC}"
    echo -e "${YELLOW}----------------------------------------${NC}"
    sed -n '1,50p' "$CONFIG_PATH" | sed -e 's/^/  /'
    echo -e "${YELLOW}----------------------------------------${NC}"
  else
    echo -e "${RED}❌ 未找到配置文件：${CONFIG_PATH}${NC}"
  fi
}

# 删除部署任务
delete_deploy_task() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🗑️ 删除 CI/CD 部署任务${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  select_deploy_task_interactive || return
  read -p "确认删除任务 '${TASK_NAME}' ? (y/N): " ok
  [[ "$ok" =~ ^[Yy]$ ]] || { echo -e "${YELLOW}已取消${NC}"; return; }
  
  # 删除相关文件
  rm -f "$CONFIG_PATH" "$SELECTED_CONF" 2>/dev/null
  
  # 清理systemd服务（如果只有这一个任务）
  local remaining_tasks=0
  for conf in "$TASKS_DIR"/deploy_*.conf; do
    [ -f "$conf" ] && remaining_tasks=$((remaining_tasks + 1))
  done
  
  if [ $remaining_tasks -eq 0 ]; then
    echo -e "${YELLOW}所有部署任务已删除，停止部署服务...${NC}"
    stop_deploy_service
    if command -v systemctl >/dev/null 2>&1; then
      rm -f "$SYSTEMD_DIR/deploy_tool.service"
      systemctl daemon-reload 2>/dev/null || true
    fi
  fi
  

# 完全卸载并重新安装
reinstall_deploy_tool() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🔄 完全卸载并重新安装 CI/CD 部署工具${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${YELLOW}⚠️  警告：此操作将删除所有部署任务配置和日志！${NC}"
  echo -e "${YELLOW}⚠️  包括：${NC}"
  echo -e "  • 所有CI/CD部署任务配置"
  echo -e "  • 所有部署日志文件"
  echo -e "  • 所有构建缓存文件"
  echo -e "  • 系统服务配置"
  echo -e "  • 临时文件"
  echo
  read -p "确认完全卸载并重新安装？(y/N): " ok
  [[ "$ok" =~ ^[Yy]$ ]] || { echo -e "${YELLOW}已取消${NC}"; return; }
  
  echo -e "${YELLOW}正在停止服务...${NC}"
  # 停止服务
  stop_deploy_service
  if command -v systemctl >/dev/null 2>&1; then
    rm -f "$SYSTEMD_DIR/deploy_tool.service"
    systemctl daemon-reload 2>/dev/null || true
  fi
  
  echo -e "${YELLOW}正在清理所有文件...${NC}"
  # 清理所有相关文件
  rm -rf "$BASE_DIR" 2>/dev/null || true
  rm -rf "$LOG_DIR" 2>/dev/null || true
  rm -rf "$BUILD_DIR" 2>/dev/null || true
  rm -rf "$DOCKER_DIR" 2>/dev/null || true
  
  echo -e "${YELLOW}正在重新创建目录结构...${NC}"
  # 重新创建目录结构
  mkdir -p "$TASKS_DIR" "$SCRIPTS_DIR" "$PID_DIR" "$LOG_DIR" "$BUILD_DIR" "$DOCKER_DIR"
  
  echo -e "${YELLOW}正在重新生成Python服务脚本...${NC}"
  # 重新生成Python服务脚本
  local global_port=$(ensure_global_port)
  create_deploy_python_server "$global_port"
  
  echo -e "${YELLOW}正在重新创建系统服务...${NC}"
  # 重新创建系统服务
  if command -v systemctl >/dev/null 2>&1; then
    create_deploy_systemd_service
  fi
  
  echo -e "${YELLOW}正在启动服务...${NC}"
  # 启动服务
  start_deploy_service "$global_port"
  
  echo -e "${GREEN}✅ 完全卸载并重新安装完成！${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}🎉 重新安装成功${NC}"
  echo -e "${CYAN}服务状态: ${WHITE}已启动${NC}"
  echo -e "${CYAN}服务端口: ${WHITE}${global_port}${NC}"
  echo -e "${CYAN}服务地址: ${WHITE}http://$(get_server_ip):${global_port}${NC}"
  echo -e "${CYAN}健康检查: ${WHITE}http://$(get_server_ip):${global_port}/healthz${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${YELLOW}现在可以重新创建CI/CD部署任务了${NC}"
}

# 欢迎页
show_welcome() {
  clear
  echo -e "${CYAN}"
  echo "╔══════════════════════════════════════════════════════════════╗"
  echo "║                                                              ║"
  echo "║                 CI/CD 自动部署管理器 v1.0                   ║"
  echo "║                                                              ║"
  echo "║      功能：Git Push触发自动构建、Docker镜像打包、容器部署      ║"
  echo "║      特点：支持多平台Git、智能版本管理、实时通知          ║"
  echo "║      流程：代码提交 → 自动构建 → 镜像打包 → 容器部署        ║"
  echo "║      通知：支持飞书、企业微信、钉钉等平台通知            ║"
  echo "║                                                              ║"
  echo "║                       作者：基于webhook_panel.sh改进           ║"
  echo "║                        版本：v1.0                            ║"
  echo "╚══════════════════════════════════════════════════════════════╝"
  echo -e "${NC}"
}

# 显示主菜单
show_main_menu() {
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
  echo -e "${WHITE}请选择操作：${NC}"
  echo -e "${CYAN}1.${NC} ${WHITE}新增 CI/CD 部署任务${NC}"
  echo -e "${CYAN}2.${NC} ${WHITE}查看部署任务列表${NC}"
  echo -e "${CYAN}3.${NC} ${WHITE}查看部署配置${NC}"
  echo -e "${CYAN}4.${NC} ${WHITE}测试部署任务${NC}"
  echo -e "${CYAN}5.${NC} ${WHITE}查看部署日志${NC}"
  echo -e "${CYAN}6.${NC} ${WHITE}删除部署任务${NC}"
  echo -e "${CYAN}7.${NC} ${WHITE}重装部署服务${NC}"
  echo -e "${CYAN}8.${NC} ${WHITE}退出${NC}"
  echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}

# 主函数
main() {
  # 检查依赖并在需要时安装Docker
  check_dependencies
  
  while true; do
    show_welcome
    show_main_menu
    read -p "请选择 [1-8]: " choice
    case "$choice" in
      1) add_deploy_task ;;
      2) list_deploy_tasks ;;
      3) view_deploy_config ;;
      4) test_deploy_task ;;
      5) view_deploy_logs ;;
      6) delete_deploy_task ;;
      7) reinstall_deploy_tool ;;
      8) echo -e "${GREEN}感谢使用 CI/CD 自动部署管理器！${NC}"; exit 0 ;;
      *) echo -e "${RED}❌ 无效选择${NC}" ;;
    esac
    echo -e "\n${YELLOW}按回车键继续...${NC}"; read
  done
}

# 执行主函数
main "$@"